/**
 * Project:   dtu.fmes.googleloon
 * File:      TimeOfDay.java
 * License: 
 *            This file is licensed under GNU General Public License version 3
 *            http://www.gnu.org/licenses/gpl-3.0.txt
 *
 * Copyright:	Nicolai Sonne [ NicolaiSonne@hotmail.com.com ]
 * Date:      Dec 11, 2013
 */

package dtu.fmes.loon;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import dtu.fmes.loon.model.Balloon;
import dtu.fmes.loon.model.ConstantBalloonValues;
import dtu.fmes.loon.model.Environment;
import dtu.fmes.loon.model.KillRandomBalloon;
import dtu.fmes.loon.model.Simulation;
import dtu.fmes.loon.model.SimulationState;
import dtu.fmes.loon.model.balloon.AirPumpModule;
import dtu.fmes.loon.model.balloon.BatteryModule;
import dtu.fmes.loon.model.balloon.CommunicationModule;
import dtu.fmes.loon.model.balloon.GpsModule;
import dtu.fmes.loon.model.balloon.SolarPanelModule;
import dtu.fmes.loon.model.environment.Area;
import dtu.fmes.loon.model.environment.ImportWind;
import dtu.fmes.loon.model.environment.Time;
import dtu.fmes.loon.model.environment.Wind;
import dtu.fmes.loon.utils.GsonUtil;
import dtu.fmes.loon.utils.VectorUtils;

/**
 * Standalone class for generating simulation parameters Outputs JSON file
 * 
 * @author NicolaiS
 * 
 */
public class GenerateData {

	private static String filename = "LoonData";

	public static void main(String[] args) throws Exception {
		SimulationState simulationState = new SimulationState();

		// Save and show target positions in UI
		boolean showTargetPosition = true;
		// Save and show optimal positions in UI
		boolean showOptimalPositions = false;

		// Length of simulation step in seconds
		long stepLength = 30L;
		// Simulation running time in seconds
		long runningTime = 24 * 60 * 60L; // 24 hours

		// Amount of balloons randomly generated
		int balloonAmount = 120;

		// Balloon altitude in meters
		double altitudeOptimal = 20 * 000.0; // 20 km
		// Balloon altitude variance in meters
		double altitudeVariance = 500.0;

		// Communication range in meters
		double commRange = 30000.0; // 30 km

		// Optimal range by percent of "commRange"
		double optimalPercent = 80.0;

		// Control Algorithm frequency in seconds
		double frequency = 5 * 60.0; // 5 min

		// Area size in meters
		double areaSizeX = 400 * 1000.0; // 400 km
		double areaSizeY = 100 * 1000.0; // 100 km

		// Define wind layer heights in meters
		double height1 = 19.5 * 1000;
		double height2 = 19.8 * 1000;
		double height3 = 20.2 * 1000;
		double height4 = 20.5 * 1000;

		// Define wind matrix
		// Use GenerateWinds.java to generate
		ImportWind[][][] importWinds = new ImportWind[][][] {
				{
						{ new ImportWind(height1, 11.44, 88.11), new ImportWind(height1, 13.28, 83.35), new ImportWind(height1, 16.80, 92.94),
								new ImportWind(height1, 13.16, 95.09), new ImportWind(height1, 14.15, 90.83), new ImportWind(height1, 13.83, 86.61),
								new ImportWind(height1, 16.12, 87.69), new ImportWind(height1, 13.66, 85.72) },
						{ new ImportWind(height1, 17.78, 90.20), new ImportWind(height1, 12.81, 96.38), new ImportWind(height1, 17.99, 96.34),
								new ImportWind(height1, 16.22, 94.10), new ImportWind(height1, 19.20, 95.78), new ImportWind(height1, 17.35, 85.99),
								new ImportWind(height1, 19.67, 88.44), new ImportWind(height1, 11.92, 90.42) } },
				{
						{ new ImportWind(height2, 11.70, 267.10), new ImportWind(height2, 19.96, 273.15), new ImportWind(height2, 10.14, 263.49),
								new ImportWind(height2, 18.95, 271.80), new ImportWind(height2, 16.15, 268.70), new ImportWind(height2, 10.12, 265.70),
								new ImportWind(height2, 12.12, 275.76), new ImportWind(height2, 15.30, 276.94) },
						{ new ImportWind(height2, 14.97, 266.73), new ImportWind(height2, 11.64, 271.80), new ImportWind(height2, 16.85, 267.69),
								new ImportWind(height2, 17.24, 269.00), new ImportWind(height2, 14.09, 272.22), new ImportWind(height2, 11.54, 263.10),
								new ImportWind(height2, 17.60, 269.08), new ImportWind(height2, 10.57, 272.11) } },
				{
						{ new ImportWind(height3, 10.42, 0.12), new ImportWind(height3, 18.54, 3.31), new ImportWind(height3, 11.10, -2.11),
								new ImportWind(height3, 14.12, 1.04), new ImportWind(height3, 14.88, 6.71), new ImportWind(height3, 11.96, -6.73),
								new ImportWind(height3, 17.83, 7.13), new ImportWind(height3, 17.22, 1.76) },
						{ new ImportWind(height3, 13.49, -3.08), new ImportWind(height3, 13.95, -2.41), new ImportWind(height3, 15.12, -3.65),
								new ImportWind(height3, 15.75, 4.90), new ImportWind(height3, 11.58, 6.71), new ImportWind(height3, 11.06, -3.73),
								new ImportWind(height3, 19.38, -5.29), new ImportWind(height3, 13.60, -2.47) } },
				{
						{ new ImportWind(height4, 18.65, 179.52), new ImportWind(height4, 12.92, 173.95), new ImportWind(height4, 16.14, 181.51),
								new ImportWind(height4, 18.90, 177.56), new ImportWind(height4, 13.99, 184.52), new ImportWind(height4, 19.06, 177.73),
								new ImportWind(height4, 19.30, 183.74), new ImportWind(height4, 17.11, 173.75) },
						{ new ImportWind(height4, 15.45, 183.06), new ImportWind(height4, 16.77, 173.58), new ImportWind(height4, 13.25, 178.98),
								new ImportWind(height4, 15.12, 183.22), new ImportWind(height4, 16.53, 183.59), new ImportWind(height4, 15.80, 175.02),
								new ImportWind(height4, 14.31, 180.84), new ImportWind(height4, 13.56, 186.83) } } };

		// Register killing of random balloons
		// Start killing "killAmount" random balloons after "killTime" seconds
		int killAmount = 25;
		long killTime = 6 * 60 * 60L; // 6 hours

		List<KillRandomBalloon> killRandomBalloons = new ArrayList<KillRandomBalloon>();
		for (int i = 1; i < killAmount; i++) {
			killRandomBalloons.add(new KillRandomBalloon(270000.0, 320000.0, 35000.0, 65000.0, killTime));
		}

		// Do not concern with the remaining
		GpsModule gps = new GpsModule(0.0, null);
		BatteryModule battery = new BatteryModule(5000.0, 5000.0, null);
		SolarPanelModule solarPanel = new SolarPanelModule(100.0, 100.0, null);
		AirPumpModule airPump = new AirPumpModule(10.0, 0.0, 10.0, 0.0, null);
		CommunicationModule communication = new CommunicationModule(commRange, (commRange * optimalPercent) / 100, frequency, 120.0, null);
		simulationState.setConstantBalloonValues(new ConstantBalloonValues(gps, battery, solarPanel, airPump, communication));

		Random rand = new Random(Double.doubleToLongBits(Math.random()));
		Double batteryLevel = 5000.0;
		List<Balloon> balloons = new ArrayList<Balloon>();
		for (int i = 1; i <= balloonAmount; i++) {
			Double latitude = rand.nextDouble() * areaSizeX;
			Double longitude = rand.nextDouble() * areaSizeY;
			Double altitude = rand.nextDouble() * altitudeVariance + (altitudeOptimal - altitudeVariance / 2);

			balloons.add(new Balloon(i, latitude, longitude, altitude, batteryLevel));
		}

		simulationState.setBalloons(balloons);

		Area area = new Area(areaSizeX, areaSizeY);
		Time time = new Time();
		time.setNightStartTime(0);
		time.setNightEndTime(0);
		Wind[][][] winds = VectorUtils.toWindArray(importWinds);

		simulationState.setEnvironment(new Environment(area, time, winds));

		Simulation simulation = new Simulation();
		simulation.setShowTargetPosition(showTargetPosition);
		simulation.setShowOptimailPositions(showOptimalPositions);
		simulation.setSimulationInterval(stepLength);
		simulation.setSimulationLength(runningTime);
		simulation.setKillRandomBalloons(killRandomBalloons);

		simulationState.setSimulation(simulation);

		// Write file
		// Filename is specified at the top
		FileWriter fw = null;
		try {
			fw = new FileWriter("../" + filename);
			GsonUtil.buildGson().toJson(simulationState, fw);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			throw e;
		} finally {
			if (fw != null) {
				try {
					fw.close();
				} catch (IOException e) {
					System.err.println(e.getMessage());
					throw e;
				}
			}
		}
	}
}
